﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Telnet UI Application
 * Version 2.10
 * 
 * Session Dialog Class
 *      Provides the UI element for each connection telnet-text session.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Text;

using Adaptive.Network.Sockets;
using Adaptive.Foundation.Windows.Forms;
using Adaptive.Telnet.UI.Properties;

namespace Adaptive.Telnet.UI
{
    /// <summary>
    /// Provides the UI element for each connection telnet-text session.
    /// </summary>
    public partial class SessionDialog : DialogBase
    {
        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Delegate definition for in-UI thread invocation.
        /// </summary>
        /// <param name="newData"></param>
        private delegate void AddRecievedTextPointer(byte[] newData);
        /// <summary>
        /// Sockets client instance.
        /// </summary>
        private SocketsClient _client;
        /// <summary>
        /// Connection definition to use.
        /// </summary>
        private ConnectionDefinition _definition;
        /// <summary>
        /// Application settings.
        /// </summary>
        private Settings _appSettings;
        /// <summary>
        /// Buffer for keystroke storage.
        /// </summary>
        private StringBuilder _internalBuffer;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public SessionDialog() : base()
        {
            InitializeComponent();
            _internalBuffer = new StringBuilder();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_client != null)
                    CloseConnection();
                    
                if (components != null)
                    components.Dispose();
            }

            _appSettings = null;
            _internalBuffer = null;
            _definition = null;
            _client = null;
            components = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the reference to the sockets client instance in use.
        /// </summary>
        /// <value>
        /// A <see cref="SocketsClient"/> instance if connected; otherwise, <b>false</b>.
        /// </value>
        public SocketsClient Client
        {
            get { return _client; }
        }
        /// <summary>
        /// Gets or sets the reference to the underlying connection definition.
        /// </summary>
        /// <value>
        /// A <see cref="ConnectionDefinition"/> instance.
        /// </value>
        public ConnectionDefinition ConnectionDef
        {
            get { return _definition; }
            set { _definition = value; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Closes the underlying socket connection, if present.
        /// </summary>
        public void CloseConnection()
        {
            if (_client != null)
            {
                _client.ConnectionEstablished -= new EventHandler(HandleConnectEstablished);
                _client.SocketException -= new SocketErrorEventHandler(HandleSocketError);
                _client.Disconnected -= new EventHandler(HandleDisconnect);
                _client.DataReceived -= new DataReceivedEventHandler(HandleDataReception);
                _client.Close();
                _client.Dispose();
                _client = null;
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Creates and assigns the event handlers for the controls on the dialog.
        /// </summary>
        protected override void AssignEventHandlers()
        {
            SessionText.KeyPress +=new KeyPressEventHandler(HandleKeyPress);
            CloseButton.Click +=new EventHandler(HandleCloseClick);
        }
        /// <summary>
        /// Removes the event handlers for the controls on the dialog.
        /// </summary>
        protected override void RemoveEventHandlers()
        {
            SessionText.KeyPress -= new KeyPressEventHandler(HandleKeyPress);
            CloseButton.Click -= new EventHandler(HandleCloseClick);
            _appSettings.PropertyChanged -= new PropertyChangedEventHandler(HandleSettingsChange);
        }
        /// <summary>
        /// Launches the background connection thread.
        /// </summary>
        protected override void StartFormThreads()
        {
            Thread connectThread = null;

            ConnectionPanel.Visible = true;
            prgDisplay.Value = 100;
            prgDisplay.Visible = true;
            SessionText.Visible = false;
            Size = new Size(388, 148);
            Refresh();
            FormBorderStyle = FormBorderStyle.FixedSingle;

            connectThread = new Thread(new ThreadStart(RunConnectionThread));
            connectThread.IsBackground = true;
            connectThread.Start();
        }
        /// <summary>
        /// Sets the initial dialog content from the app and definitions settings.
        /// </summary>
        protected override void SetInitialContent()
        {
            _appSettings = new Settings();
            _appSettings.PropertyChanged +=new PropertyChangedEventHandler(HandleSettingsChange);

            if (_definition.Font != null)
                SessionText.Font = _definition.Font;
            else
            {
                SessionText.Font = AppUtilities.GetApplicationDefaultFont();
            }
            
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Event Handlers
         *------------------------------------------------------------------------------*/
        #region Private Event Handlers
        /// <summary>
        /// Handles the event when the user clicks the close button on the toolbar.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">An <see cref="EventArgs"/> event arguments instance.</param>
        private void HandleCloseClick(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;
            CloseConnection();
            Close();

        }
        /// <summary>
        /// Handles the event when the user enters a keystroke in the text box.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">
        /// A <see cref="KeyPressEventArgs"/> event arguments instance containing the character code.
        /// </param>
        private void HandleKeyPress(object sender, KeyPressEventArgs e)
        {
            string data = null;

            SessionText.SelectionStart = SessionText.Text.Length;
            if (_definition.SendAsTyped)
            {
                if (e.KeyChar == 13)
                    data = "\r\n";
                else
                    data += (char)e.KeyChar;

                if ((_client != null) && (_client.Connected))
                    _client.Send(data, true);
            }
            else
            {
                if (e.KeyChar == 13)
                {
                    _internalBuffer.Append("\r\n");
                    data = _internalBuffer.ToString();
                    _internalBuffer = new StringBuilder();
                    _client.Send(data, true);
                }
                else if (e.KeyChar == 8)
                {
                    //Handle the backspace.
                    _internalBuffer.Remove(_internalBuffer.Length, 1);
                }
                else
                {
                    _internalBuffer.Append((char)e.KeyChar);
                }
            }
            SessionText.SelectionStart = SessionText.Text.Length;
        }
        /// <summary>
        /// handles the event when the application settings are modified.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">
        /// A <see cref="PropertyChangedEventArgs"/> indicating the property that was changed.
        /// </param>
        private void HandleSettingsChange(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                default:
                    break;
            }
        }
        private void HandleConnectEstablished(object sender, EventArgs e)
        {
        }
        /// <summary>
        /// Handles the event when the client unexpectedly disconnects.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">An <see cref="EventArgs"/> event arguments instance.</param>
        private void HandleDisconnect(object sender, EventArgs e)
        {
            CloseConnection();
            SetFormForDisconnect();
        }
        /// <summary>
        /// Handles the event when the a socket error is encountered.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">An <see cref="SocketErrorEventArgs"/> event arguments instance.</param>
        private void HandleSocketError(object sender, SocketErrorEventArgs e)
        {
            MessageBox.Show(e.SocketException.Message, "Socket Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        /// <summary>
        /// Handles the event when ne data is received.
        /// </summary>
        /// <param name="sender">The object raising the event.</param>
        /// <param name="e">An <see cref="DataReceivedEventArgs"/> event arguments instance.</param>
        private void HandleDataReception(object sender, DataReceivedEventArgs e)
        {
            AddRecievedTextPointer functionptr = null;

            functionptr = new AddRecievedTextPointer(AddNewText);
            Invoke(functionptr, new object[] { e.DataRecieved });

        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Adds received text to the session.
        /// </summary>
        /// <param name="newData">
        /// A byte array containing the new data.
        /// </param>
        private void AddNewText(byte[] newData)
        {
            SessionText.Text += System.Text.ASCIIEncoding.ASCII.GetString(newData);
            SessionText.SelectionStart = SessionText.Text.Length;
        }
        /// <summary>
        /// Executes the connection in the background.
        /// </summary>
        private void RunConnectionThread()
        {
            bool connected = false;

            //Sleep for a bit to allow the form to display.
            Thread.Sleep(100);
            _client = new SocketsClient();
            _client.ConnectionEstablished +=new EventHandler(HandleConnectEstablished);
            _client.SocketException +=new SocketErrorEventHandler(HandleSocketError);

            connected = _client.Connect(_definition.RemoteEndPoint.Address, (short)_definition.RemoteEndPoint.Port);

            if (_client.Connected)
            {
                _client.Disconnected +=new EventHandler(HandleDisconnect);
                _client.DataReceived +=new DataReceivedEventHandler(HandleDataReception);

                Invoke(new MethodInvoker(SetFormForConnect));
            }
            else
            {
                _client.ConnectionEstablished -= new EventHandler(HandleConnectEstablished);
                _client.SocketException -= new SocketErrorEventHandler(HandleSocketError);
                _client.Dispose();
                _client = null;
                Invoke(new MethodInvoker(SetFormForDisconnect));
            }
        }
        /// <summary>
        /// Sets the dialog into a connected session state.
        /// </summary>
        private void SetFormForConnect()
        {
            ConnectionPanel.Visible = false;
            SessionText.Visible = true;
            FormBorderStyle = FormBorderStyle.Sizable;
            Size = new Size(800, 600);
        }
        /// <summary>
        /// Sets the dialog into a connection-failed session state.
        /// </summary>
        private void SetFormForDisconnect()
        {
            ConnectionPanel.Visible = true;
            WaitLabel.Text = "-- Could not create connection --";
            prgDisplay.Visible = false;
            SessionText.Visible = false;
        }
        #endregion

    }
}
